.SUFFIXES: .g .c .o

# 见文件 mk
# gcc, tcc, clang ...
CC = gcc
# 0, 1
DEBUG = 1

-include mk.inc

JIT ?= 1
# TODO hsq -V 导致 test/flow/except.pn 失败，段错误，不论是否 JIT
# TODO hsq -V 导致 test/tables/big.pn 失败，段错误
JIT_MCACHE = 0
# TODO hsq 搜索、消除 JIT_DEBUG
JIT_DEBUG = 0
DYLIB = 1

CFLAGS += -std=c99 -fPIC
CFLAGS += -DALWAYS_ASSERT

# ifeq (${CC}@${OS},tcc@Darwin)
#   CFLAGS += -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
# endif

# TODO hsq 这些 -Wno-XXX 暗示代码写得不好。
CFLAGS += -Wall -Wextra
# CFLAGS += -fno-strict-aliasing
# CFLAGS += -Wno-return-type
# CFLAGS += -Wno-sign-compare
# mac: 3
CFLAGS += -Wno-shift-negative-value
# mac: 600+
CFLAGS += -Wno-unused-parameter
# linux: XXXX
# CFLAGS += -Wno-ignored-qualifiers

# TODO hsq -pedantic 很多警告
# CFLAGS += -pedantic
# mac: XXXX
# CFLAGS += -Wno-dollar-in-identifier-extension

# TODO: -O2 doesn't include -fno-stack-protector
CFLAGS += ${DEBUGFLAGS}

ifeq (${JIT_MCACHE},1)
  CFLAGS += -DJIT_MCACHE
endif

ifeq (${JIT_DEBUG},1)
  CFLAGS += -DJIT_DEBUG
endif

CORE = asm.c ast.c callcc.c compile.c contrib.c file.c gc.c \
	internal.c lick.c mt19937ar.c number.c module.c objmodel.c \
	primitive.c string.c syntax.c table.c vm.c
SRC = $(patsubst %,core/%,${CORE})

ifeq (${JIT},1)
  SRC += core/vm-${TARGET}.c
endif

LIB_LINEEDIT = lib/lineedit.c 3rd/linenoise.c

ifeq (${DYLIB},0)
  SRC += ${LIB_LINEEDIT}
else
  CFLAGS += -DDYLIB
  ifeq (${OS},Darwin)
    DYLIB_FLAGS = -bundle -undefined dynamic_lookup
  else
    DYLIB_FLAGS = -shared -fPIC
  endif

  DYLIBS =
  OBJ_DYLIBS =

  DYLIBS += lineedit.so
  OBJ_LINEEDIT = $(patsubst %,objs/%,${LIB_LINEEDIT:.c=.o})
  OBJ_DYLIBS += ${OBJ_LINEEDIT}
endif

# OBJ = ${SRC:.c=.o}
OBJ = $(patsubst %,objs/%,${SRC:.c=.o})
OBJ_POTION = objs/core/potion.o
OBJ_TEST = objs/test/api/potion-test.o objs/test/api/CuTest.o
OBJ_GC_TEST = objs/test/api/gc-test.o objs/test/api/CuTest.o
OBJ_GC_BENCH = objs/test/api/gc-bench.o

DOC = doc/start.textile
DOCHTML = ${DOC:.textile=.html}

PREFIX = /usr/local

# NOTE /bin/echo 无颜色，-e 选项失效，被作为内容输出。
ECHO_COLOR = echo
GREG = objs/tools/greg
INCS = -Icore -I3rd
# JIT ?= 1
LIBS = -lm -ldl

RELEASE ?= ${VERSION}.${REVISION}
PKG := "potion-${RELEASE}"

all: potion ${DYLIBS}
	# +${MAKE} -s usage

pnb:
	@${ECHO} compile preluge
	@./potion -c preluge.pn
	@${ECHO} compile repl
	@./potion -c repl.pn

lineedit.so: ${OBJ_LINEEDIT}
	@${ECHO} LINK lineedit.so
	@${CC} ${DYLIB_FLAGS} $^ -o lineedit.so
	@if [ "${DEBUG}" != "1" ]; then \
		${ECHO} STRIP lineedit.so; \
		${STRIP} lineedit.so; \
	fi

# NOTE 依赖 core/config.h ，避免使用 gcc 生成的；反之也是。
tcc-help:
	@${ECHO} make tcc args='"[options] [script] [arguments]"'
tcc:
	@${ECHO} 依赖 core/config.h ，避免使用 gcc 生成的；反之也是。
	@#rm -f core/config.h
	@${MAKE} -s clean CC=tcc
	@${MAKE} -s core/config.h CC=tcc
	@${MAKE} -s core/syntax.c CC=tcc
	@tcc ${CFLAGS} ${INCS} -run tcc-run.c ${args}

rebuild: clean potion test

usage:
	@${ECHO} " "
	@${ECHO} " ~ using potion ~"
	@${ECHO} " "
	@${ECHO} " Running a script."
	@${ECHO} " "
	@${ECHO} "   $$ ./potion example/fib.pn"
	@${ECHO} " "
	@${ECHO} " Dump the AST and bytecode inspection for a script. "
	@${ECHO} " "
	@${ECHO} "   $$ ./potion -V example/fib.pn"
	@${ECHO} " "
	@${ECHO} " Compiling to bytecode."
	@${ECHO} " "
	@${ECHO} "   $$ ./potion -c example/fib.pn"
	@${ECHO} "   $$ ./potion example/fib.pnb"
	@${ECHO} " "
	@${ECHO} " Potion builds its JIT compiler by default, but"
	@${ECHO} " you can use the bytecode VM by running scripts"
	@${ECHO} " with the -B flag."
	@${ECHO} " "
	@${ECHO} " If you built with JIT=0, then the bytecode VM"
	@${ECHO} " will run by default."
	@${ECHO} " "
	@${ECHO} " To verify your build,"
	@${ECHO} " "
	@${ECHO} "   $$ make test"
	@${ECHO} " "
	@${ECHO} " Kindly email your respects and thoughtful queries"
	@${ECHO} " to _why <why@whytheluckystiff.net>. Thankyou."

mk.inc: mk Makefile
	@${MAKE} CC=${CC} DEBUG=${DEBUG} -f mk

config:
	@${ECHO} "#define POTION_CC         \"${CC}\""
	@${ECHO} "#define POTION_CFLAGS     \"${CFLAGS}\""
	@${ECHO} "#define POTION_DEBUG      ${DEBUG}"
	@${ECHO} "#define POTION_JIT        ${JIT}"
	@${ECHO} "#define POTION_MAKE       \"${MAKE}\""
	@${ECHO} "#define POTION_PREFIX     \"${PREFIX}\""
	@${ECHO}
	@./tools/config.sh ${CC}

core/version.h:
	@${ECHO} "#define POTION_DATE       \"${DATE}\"" > core/version.h
	@${ECHO} "#define POTION_COMMIT     \"${COMMIT}\"" >> core/version.h
	@${ECHO} "#define POTION_REV        ${REVISION}" >> core/version.h
	@${ECHO} >> core/version.h

core/config.h: core/version.h
	@${ECHO} MAKE $@
	@cat core/version.h > core/config.h
	@${MAKE} -s config >> core/config.h

# ifneq (${CC},tcc)
# tcc 没 -fno-omit-frame-pointer ，但加上报错：
#	core/callcc.c:10: error: include file 'stdio.h' not found
objs/core/callcc.o: core/callcc.c
	@${ECHO} CC $< +frame-pointer
	@${CC} -c -fno-omit-frame-pointer ${INCS} -o $@ $<
# endif

objs/core/vm-x86.o: core/vm-x86.c core/vm-x86-asm.c core/config.h
	@${ECHO} CC $<
	@${CC} -c ${CFLAGS} ${INCS} -o $@ $<

objs/%.o: %.c core/config.h
	@${ECHO} CC $<
	@${CC} -c ${CFLAGS} ${INCS} -o $@ $<

.c.o: core/config.h
	@${ECHO} CC $<
	@${CC} -c ${CFLAGS} ${INCS} -o $@ $<

%.c: %.g ${GREG}
	@${ECHO} GREG $<
	@${GREG} $< > $@

.g.c: ${GREG}
	@${ECHO} GREG $<
	@${GREG} $< > $@

# -O3 -DNDEBUG
${GREG}: tools/greg.c tools/compile.c tools/tree.c
	@${ECHO} CC $@
	@${CC} ${CFLAGS} -o $@ tools/greg.c tools/compile.c tools/tree.c -Itools

potion: ${OBJ_POTION} ${OBJ}
	@${ECHO} LINK potion
	@${CC} ${CFLAGS} ${OBJ_POTION} ${OBJ} ${LIBS} -o potion
	@if [ "${DEBUG}" != "1" ]; then \
		${ECHO} STRIP potion; \
		${STRIP} potion; \
	fi

# Linux 中的 /bin/sh 没有内置 time 功能，改用 bash 。
# bench: SHELL = /bin/bash
bench: potion objs/test/api/gc-bench
	@${ECHO}; \
	${ECHO} running GC benchmark; \
	bash -c "time objs/test/api/gc-bench"

test: potion objs/test/api/potion-test objs/test/api/gc-test
	@${ECHO}; \
	TEST() { ${ECHO_COLOR} "running \033[1;35m$$1\033[0m tests"; }; \
	TEST API; \
	objs/test/api/potion-test; \
	TEST GC; \
	objs/test/api/gc-test; \
	count=0; failed=0; pass=0; \
	while [ $$pass -lt 3 ]; do \
		${ECHO}; \
		if [ $$pass -eq 0 ]; then \
			TEST VM; \
		elif [ $$pass -eq 1 ]; then \
			TEST compiler; \
		else \
			TEST JIT; \
			jit=`./potion -v | sed "/jit=1/!d"`; \
			if [ "$$jit" = "" ]; then \
				${ECHO} skipping; \
				break; \
			fi; \
		fi; \
		for f in test/**/*.pn; do \
			look=`cat $$f | sed "/\#/!d; s/.*\# //"`; \
			if [ $$pass -eq 0 ]; then \
				for=`./potion -I -B $$f | sed "s/\n$$//"`; \
			elif [ $$pass -eq 1 ]; then \
				./potion -c $$f > /dev/null; \
				fb="$$f"b; \
				for=`./potion -I -B $$fb | sed "s/\n$$//"`; \
				rm -rf $$fb; \
			else \
				for=`./potion -I -J $$f | sed "s/\n$$//"`; \
			fi; \
			if [ "$$look" != "$$for" ]; then \
				${ECHO}; \
				${ECHO} "$$f: expected <$$look>, but got <$$for>"; \
				failed=`expr $$failed + 1`; \
			else \
				${ECHO} -n .; \
			fi; \
			count=`expr $$count + 1`; \
		done; \
		pass=`expr $$pass + 1`; \
	done; \
	${ECHO}; \
	if [ $$failed -gt 0 ]; then \
		${ECHO_COLOR} "$$failed \033[1;31mFAILS\033[0m ($$count tests)"; \
	else \
		${ECHO_COLOR} "\033[1;32mOK\033[0m ($$count tests)"; \
	fi

objs/test/api/potion-test: ${OBJ_TEST} ${OBJ}
	@${ECHO} LINK potion-test
	@${CC} ${CFLAGS} ${OBJ_TEST} ${OBJ} ${LIBS} -o $@

objs/test/api/gc-test: ${OBJ_GC_TEST} ${OBJ}
	@${ECHO} LINK gc-test
	@${CC} ${CFLAGS} ${OBJ_GC_TEST} ${OBJ} ${LIBS} -o $@

objs/test/api/gc-bench: ${OBJ_GC_BENCH} ${OBJ}
	@${ECHO} LINK gc-bench
	@${CC} ${CFLAGS} ${OBJ_GC_BENCH} ${OBJ} ${LIBS} -o $@

tarball: core/version.h core/syntax.c
	mkdir -p pkg
	rm -rf ${PKG}
	git checkout-index --prefix=${PKG}/ -a
	rm -f ${PKG}/.gitignore
	cp core/version.h ${PKG}/core/
	cp core/syntax.c ${PKG}/core/
	tar czvf pkg/${PKG}.tar.gz ${PKG}
	rm -rf ${PKG}

%.html: %.textile
	@${ECHO} DOC $<
	@${ECHO} "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"DTD/xhtml1-transitional.dtd\">" > $@
	@${ECHO} "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">" >> $@
	@${ECHO} "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />" >> $@
	@${ECHO} "<style type=\"text/css\">@import 'doc.css';</style>" >> $@
	@${ECHO} "<div id='potion'><img src='potion-1.png' /></div>" >> $@
	@${ECHO} "</head><body><div id='central'>" >> $@
	@redcloth $< >> $@
	@${ECHO} "</div></body></html>" >> $@

doc: ${DOCHTML}

sloc: clean
	@cp core/syntax.g core/syntax-g.c
	@sloccount core
	@rm -f core/syntax-g.c

todo:
	@grep -rInso 'TODO: \(.\+\)' core

clean:
	@${ECHO} cleaning
	@rm -f ${OBJ} ${OBJ_POTION} ${OBJ_TEST} ${OBJ_GC_TEST} ${OBJ_GC_BENCH}
	#@rm -f ${DOCHTML}
	@rm -f ${GREG} ${GREG}.o tools/compile.o tools/tree.o
	@rm -f core/config.h core/version.h core/syntax.c
	@rm -f potion potion.exe objs/test/api/potion-test objs/test/api/gc-test objs/test/api/gc-bench
	@rm -f ${DYLIBS} ${OBJ_DYLIBS}
	@find . -name '*.dSYM' | xargs rm -fr

.PHONY: all clean doc rebuild test
